
#include "../../include/GPCompute.h"

#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include <stdio.h>

void DoCounter( HWND hWnd ) {
	static DWORD t;
	static LONG count;
	DWORD currtime = GetTickCount();
	if ( t==0 ) t = currtime;
	if ( t + 1000 < currtime ) { static int i;
		static char wndtxt[260], strfps[260];
		if (i==0) { i=1; GetWindowTextA( hWnd, wndtxt, MAX_PATH ); }
		wsprintfA( strfps, "%s - FPS:%d", wndtxt, count*1000/(currtime-t) );
		SetWindowTextA( hWnd, strfps );
		t += 1000; count = 0;
	}
	++count;
}

HRESULT LoadShader( LPDWORD* pdwShader, wchar_t const* filename )
{
	HANDLE p, f;
	p = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( INVALID_HANDLE_VALUE == p ) {
		return E_FAIL;
	}
	f = CreateFileMappingW( p, NULL, PAGE_READONLY, 0, 0, NULL );
	if ( NULL == f ) {
		return E_FAIL;
	}
	*pdwShader = (LPDWORD)MapViewOfFile( f, FILE_MAP_READ, 0, 0, 0 );
	if ( NULL == *pdwShader ) {
		return E_FAIL;
	}
	CloseHandle( f );
	CloseHandle( p );
	return S_OK;
}
void UnloadShader( DWORD* pdwShader )
{
	UnmapViewOfFile( (LPCVOID)pdwShader );
}
int Powerof2( int src )
{
	int n = 1;
	while ( src > n ) n <<= 1;
	return n;
}

static int width, height, framerate, refresh=0;

static void LoadConfig( wchar_t filename[/*MAX_PATH*/], wchar_t filevso[/*filevso*/], wchar_t filepso[/*filepso*/] ) {
	GetPrivateProfileStringW( L"PlayConfig", L"Filename", L"1.nv12", filename, MAX_PATH, L"./Config.ini" );
	width		= GetPrivateProfileIntW( L"PlayConfig", L"Width", 640, L"./Config.ini" );
	height		= GetPrivateProfileIntW( L"PlayConfig", L"Height", 480, L"./Config.ini" );
	framerate	= GetPrivateProfileIntW( L"PlayConfig", L"Framerate", 25, L"./Config.ini" );
	GetPrivateProfileStringW( L"ShadConfig", L"Vertex", L"NV12Gener.vso", filevso, MAX_PATH, L"./Config.ini" );
	GetPrivateProfileStringW( L"ShadConfig", L"Pixel",  L"NV12Gener.pso", filepso, MAX_PATH, L"./Config.ini" );
}

static void GenerateBlank( void * y, void * vu )
{
	memset( y,  0x60, width * height );
	memset( vu, 0x80, width * height /2 );
}

static void CopyData( HGPGPU hgpgpu, HGPVRRAY hvrray, HGPARRAY harray, void const* buf, int wbytes, int h )
{
	int ret;
	void* data; long pitch;

	ret = GpComputeGetPointer( harray, &data, &pitch );
	if (0 <= ret) {
		LPCBYTE pSrc = (LPCBYTE)buf; 
		LPBYTE pData = (LPBYTE)data;
		int i;

		for ( i=0; i<h; ++i ) {
			memcpy( pData, pSrc, wbytes );
			pSrc += wbytes, pData += pitch;
		}

		ret = GpComputeSubmit( hgpgpu, harray, hvrray, 0, 0 );
		if ( ret < 0 ) OutputDebugStringA( "Failed Submit VideoMemory!\n" );
	} else {
		OutputDebugStringA( "Failed Get Pointer!\n" );
	}
}

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
		case WM_TIMER:
			refresh = 1;
			return 0;

		case WM_CLOSE:
			PostQuitMessage( 0 );
			return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE _1, LPSTR _2, INT cmdShow )
{
	HGPGPU hgpgpu;
	HWND hWnd; GP_CONTEXT ctx; RECT rect = { 0, };
	wchar_t filename[MAX_PATH];
	wchar_t filevso[MAX_PATH], filepso[MAX_PATH];
//	int texwidth, texheight;

	// Register the window class
    WNDCLASSEXW wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, /*hInst*/NULL, NULL, NULL, NULL, NULL,
                      L"GP Tutorial", NULL };
	(void*)(_1, _2);
	wc.hInstance = hInst; 
    RegisterClassExW( &wc );

	LoadConfig( filename, filevso, filepso );

    // Create the application's window
	hWnd = CreateWindowW( L"GP Tutorial", L"NV12 Viewer",
                      WS_OVERLAPPEDWINDOW, 0, 0, width, height, GetDesktopWindow(), NULL, wc.hInstance, NULL );
	rect.right = width, rect.bottom = height;

	// adjust window size to fix size ... 
	AdjustWindowRect( &rect, WS_OVERLAPPEDWINDOW, FALSE );
	MoveWindow( hWnd, 300+rect.left, 300+rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );

	// Prepare the context : 
	ctx.width = width;
	ctx.height = height;
	ctx.colortype = TYPE_COLORALPHA; // using D3DFMT_A8R8G8B8
	ctx.colorprecision = PREC_IN_32; 
	ctx.depthtype = TYPE_DEPTH;
	ctx.depthprecision = PREC_IN_16; // using D3DFMT_D16
	ctx.canvas = (void*)hWnd;

    // Initialize Direct3D
    if( GpComputeInit( &hgpgpu, &ctx, 0 ) >= 0 )
    {
        MSG msg;
		FILE* fpnv12;
		void* bufnv12, *y, *vu;
		int const LENGTH = width * height * 3 / 2;
		int ret, texw, texh;

		HGPARRAY harrayY, harrayVU;
		HGPVRRAY hvrrayY, hvrrayVU; 
		DWORD vshid, pshid; LPDWORD p;
		GP_CALCSTATE state = {0}; 
		GP_RECT rc; HGPSHAPERECT vrc;
		
		// load resource 
		fpnv12 = _wfopen( filename, L"rb" );
		if ( NULL == fpnv12 ) OutputDebugStringA( "Failed Load NV12File!\n" );
		bufnv12 = malloc( LENGTH );
		y = (char*)bufnv12 + 0;
		vu = (char*)bufnv12 + width * height;
		if ( NULL == fpnv12 ) GenerateBlank( y, vu );
		texw = Powerof2(width);
		texh = Powerof2(height);

		// texture prepare 
		ret = GpComputeAllocate( hgpgpu, texw, texh, TYPE_LUMINFULL, PREC_IN_08, OPMODE_READ, &harrayY );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate Memory For Y!\n" );
		ret = GpComputeAllocateVram( hgpgpu, texw, texh, TYPE_LUMINFULL, PREC_IN_08, OPMODE_READ, &hvrrayY );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate VideoMemory For Y!\n" );
		ret = GpComputeAllocate( hgpgpu, texw/2, texh/2, TYPE_LUMINFULL, PREC_IN_16_08, OPMODE_READ, &harrayVU );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate memory For UV!\n" );
		ret = GpComputeAllocateVram( hgpgpu, texw/2, texh/2, TYPE_LUMINFULL, PREC_IN_16_08, OPMODE_READ, &hvrrayVU );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate VideoMemory For VU!\n" );
		
		ret = GpComputeSelectSourceUnit( hgpgpu, hvrrayY, 0 ); 
		if ( ret < 0 ) OutputDebugStringA( "Failed SetTexture!\n" );
		ret = GpComputeSelectSourceUnit( hgpgpu, hvrrayVU, 1 ); 
		if ( ret < 0 ) OutputDebugStringA( "Failed SetTexture!\n" );
		
		// vertex prepare 
		rc.pt1.x = -1.0f, rc.pt1.y = 1.0f;
		rc.pt2.x =  (2.0f * texw + 1.0f) / width -1.0f, rc.pt2.y = 1.0f - (2.0f * texh + 1.0f)/ height;
		ret = GpComputeShapeRect( hgpgpu, &rc, 1, &vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create Vertex Rectangle!\n" );
		ret = GpComputeSelectSourceShape( hgpgpu, vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed SetStreamSource and Index!\n" );

		// shader programe 
		if (FAILED(LoadShader( &p, filevso ))) 
			OutputDebugStringA( "Failed Allocate Load VertexShader!\n" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_VERTEX, &vshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create VertexShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select VertexShader!\n" );

		if (FAILED(LoadShader( &p, filepso )))
			OutputDebugStringA( "Failed Allocate Load PixelShader" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_PIXEL, &pshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create PixelShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select PixelShader!\n" );

		// render state : 
		state.red_write = state.green_write = state.blue_write = 1;
		ret = GpComputeSetProp( hgpgpu, &state );
		if ( ret < 0 ) OutputDebugStringA( "Failed Set RenderState!\n" );

		// Show the window
		ShowWindow( hWnd, cmdShow );
		UpdateWindow( hWnd );

        // Enter the message loop
        ZeroMemory( &msg, sizeof(msg) );

		SetTimer( hWnd, 0, 1000/framerate, NULL );
        while( msg.message!=WM_QUIT )
        {
            if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
            else
            {
				GP_PREPARE prepare = {0};
				DoCounter( hWnd );

				// LOAD file 
				if (refresh) { 
					size_t cnt = 1;
					// wrapback 
					refresh = 0; // reset flag ... 
					if ( fpnv12 ) {
						cnt = fread( bufnv12, LENGTH, 1, fpnv12 );
						if ( cnt <= 0 ) { fseek( fpnv12, 0L, SEEK_SET ); cnt = fread( bufnv12, width * height * 3 / 2, 1, fpnv12 ); }
					}
					// copy Y part 
					CopyData( hgpgpu, hvrrayY, harrayY, y, width, height );
					CopyData( hgpgpu, hvrrayVU, harrayVU, vu, width, height/2 );
				}

				// RENDER 
				ret = GpComputeBeginTarget( hgpgpu, NULL, &prepare ); // RTT
				if ( ret < 0 ) OutputDebugStringA( "Failed BeginScene!\n" );
				ret = GpComputeCalculateRect( hgpgpu, 0, 0, NULL ); // draw all 
				if ( ret < 0 ) OutputDebugStringA( "Failed RenderToTexture!\n" );
				ret = GpComputeEndTarget( hgpgpu, 1 );
				if ( ret < 0 ) OutputDebugStringA( "Failed EndScene!\n" );
            }
        }
		KillTimer( hWnd, 0 );

		GpComputeDeleteProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		GpComputeDeleteProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		
		GpComputeDeallocateVram( hgpgpu, hvrrayVU );
		GpComputeDeallocateVram( hgpgpu, hvrrayY );
		GpComputeDeallocate( hgpgpu, harrayVU );
		GpComputeDeallocate( hgpgpu, harrayY );

		GpComputeShapeFree( hgpgpu, vrc );

		if (fpnv12) fclose( fpnv12 );
		free( bufnv12 );

		GpComputeFree( hgpgpu );
    }

	DestroyWindow( hWnd );
    // Clean up everything and exit the app
    UnregisterClassA( "GP Tutorial", wc.hInstance );
    return 0;
}
